package org.proteored.miapeapi.experiment.model;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.proteored.miapeapi.cv.ControlVocabularyManager;
import org.proteored.miapeapi.cv.ms.MSFileType;
import org.proteored.miapeapi.experiment.model.datamanager.DataManager;
import org.proteored.miapeapi.experiment.model.datamanager.ReplicateDataManager;
import org.proteored.miapeapi.experiment.model.filters.FDRFilter;
import org.proteored.miapeapi.experiment.model.filters.Filter;
import org.proteored.miapeapi.interfaces.ms.MiapeMSDocument;
import org.proteored.miapeapi.interfaces.ms.ResultingData;
import org.proteored.miapeapi.interfaces.ms.Spectrometer;
import org.proteored.miapeapi.interfaces.msi.InputParameter;
import org.proteored.miapeapi.interfaces.msi.MiapeMSIDocument;
import org.proteored.miapeapi.spring.SpringHandler;
import org.proteored.miapeapi.util.MiapeReportsLinkGenerator;

/**
 * Class that represents a replicate. A replicate contains a MIAPE MS and a
 * MIAPE MSI document associated.
 * 
 * @author Salva
 */
public class Replicate implements IdentificationSet<Void> {
	// log
	private static final Logger log = Logger
			.getLogger("log4j.logger.org.proteored");
	// MIAPE MS
	private List<MiapeMSDocument> miapeMSs;
	// MIAPE MSI
	private final List<MiapeMSIDocument> miapeMSIs;

	protected ReplicateDataManager dataManager;

	private String replicateName;

	private final String experimentName;
	private Experiment previousLevelIdentificationSet;
	private final ControlVocabularyManager cvManager;
	private final Integer minPeptideLength;

	// Identification set

	// public Replicate(String name, String experimentName,
	// List<MiapeMSDocument> miapeMSs, List<MiapeMSIDocument> miapeMSIs,
	// List<Filter> filters, ControlVocabularyManager cvManager) {
	// this(name, experimentName, miapeMSs, miapeMSIs, filters, null,
	// cvManager);
	// }

	/**
	 * 
	 * @param name
	 *            name of the replicate
	 * @param experimentName
	 * @param sortingProteins
	 *            The sorting parameters needed to sort the proteins
	 * @param sortingPeptides
	 *            The sorting parameters needed to sort the peptides
	 * @param miapeMSs
	 *            associated MIAPE MS
	 * @param miapeMSIs
	 *            associated MIAPE MSI
	 */
	public Replicate(String name, String experimentName,
			List<MiapeMSDocument> miapeMSs, List<MiapeMSIDocument> miapeMSIs,
			List<Filter> filters, Integer minPeptideLength,
			ControlVocabularyManager cvManager) {
		this.experimentName = experimentName;
		this.replicateName = name;
		this.minPeptideLength = minPeptideLength;
		// if (experimentName != null && !"".equals(experimentName))
		// this.name = this.name + "/" + experimentName;
		if (cvManager != null)
			this.cvManager = cvManager;
		else
			this.cvManager = SpringHandler.getInstance().getCVManager();
		this.miapeMSs = miapeMSs;
		this.miapeMSIs = miapeMSIs;
		this.dataManager = new ReplicateDataManager(this, miapeMSIs, filters,
				minPeptideLength);
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof IdentificationSet) {
			return obj.toString().equals(this.toString());
		}
		return false;
	}

	public String getExperimentName() {
		return experimentName;
	}

	@Override
	public String getFullName() {
		if (this.previousLevelIdentificationSet != null
				&& this.previousLevelIdentificationSet
						.getPreviousLevelIdentificationSet() != null
				&& this.previousLevelIdentificationSet
						.getPreviousLevelIdentificationSet()
						.getNumExperiments() == 1)
			return getName();
		return getName() + " / " + getExperimentName();
	}

	@Override
	public int getNumDifferentPeptides(boolean distinguishModPep) {
		return dataManager.getNumDifferentPeptides(distinguishModPep);
	}

	@Override
	public int getNumDifferentProteinGroups(boolean countNonConclusiveProteins) {
		return dataManager
				.getNumDifferentProteinGroups(countNonConclusiveProteins);
	}

	@Override
	public List<ProteinGroup> getIdentifiedProteinGroups() {
		// log.info("Getting proteins from replicate:" + this.getName());
		final List<ProteinGroup> proteinGroups = this.dataManager
				.getIdentifiedProteinGroups();
		// log.info("returning " + proteinGroups.size() +
		// " protein groups in replicate " + getName());
		return proteinGroups;

	}

	/**
	 * Gets a list of peptides (ordered by score) that pass the FDR threshold
	 * defined by a {@link FDRFilter} that should be set before this call.
	 * 
	 * @return
	 */
	@Override
	public List<ExtendedIdentifiedPeptide> getIdentifiedPeptides() {
		log.info("Getting peptides from replicate:" + this.getName());
		final List<ExtendedIdentifiedPeptide> identifiedPeptides = this.dataManager
				.getIdentifiedPeptides();
		log.info("returning " + identifiedPeptides.size()
				+ " peptides in replicate " + getName());
		return identifiedPeptides;
	}

	@Override
	public boolean hasProteinGroup(ProteinGroup proteinGroup) {
		return dataManager.hasProteinGroup(proteinGroup);
	}

	@Override
	public HashMap<String, ProteinGroupOccurrence> getProteinGroupOccurrenceList() {
		return dataManager.getProteinGroupOccurrenceList();
	}

	@Override
	public HashMap<String, PeptideOccurrence> getPeptideOccurrenceList(
			boolean distinguishModPep) {
		return dataManager.getPeptideOcurrenceList(distinguishModPep);
	}

	@Override
	public void setName(String name) {
		this.replicateName = name;

	}

	/**
	 * Gets the number of proteins identified taking into account the filters
	 * 
	 * @return
	 */
	@Override
	public int getTotalNumProteinGroups(boolean countNonConclusiveProteins) {
		return dataManager.getTotalNumProteinGroups(countNonConclusiveProteins);
	}

	public List<MiapeMSDocument> getMiapeMSs() {
		return miapeMSs;
	}

	public void setMiapeMS(List<MiapeMSDocument> miapeMSs) {
		this.miapeMSs = miapeMSs;
	}

	public List<MiapeMSIDocument> getMiapeMSIs() {
		return miapeMSIs;
	}

	@Override
	public int getPeptideOccurrenceNumber(String sequence, boolean distModPep) {
		return dataManager.getPeptideOccurrenceNumber(sequence, distModPep);
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("\t\tReplicate " + getName() + "\n");
		if (miapeMSs != null) {
			for (MiapeMSDocument ms : miapeMSs) {
				sb.append("\t\t\t\tMIAPE MS: " + ms.getName() + "\n");
			}

		}
		if (miapeMSIs != null) {
			for (MiapeMSIDocument msi : miapeMSIs) {
				sb.append("\t\t\t\tMIAPE MSI: " + msi.getName() + "\n");
			}

		}
		sb.append("\t\t\t\tNumber of protein groups:"
				+ getTotalNumProteinGroups(false) + "\n");
		sb.append("\t\t\t\tNumber of different protein groups:"
				+ getNumDifferentProteinGroups(false) + "\n");
		// sb.append("\t\t\t\tNumber of proteins:"
		// + getIdentifiedProteins().size() + "\n");
		sb.append("\t\t\t\tNumber of peptides:" + getTotalNumPeptides() + "\n");
		sb.append("\t\t\t\tNumber of different peptides:"
				+ getNumDifferentPeptides(true) + "\n");

		return sb.toString();
	}

	@Override
	public int getProteinGroupOccurrenceNumber(ProteinGroup proteinGroup) {
		List<ProteinGroup> proteinGroups = this.getIdentifiedProteinGroups();
		if (proteinGroups.contains(proteinGroup))
			return 1;

		return 0;
	}

	@Override
	public double getAverageNumDifferentProteinGroups(
			boolean countNonConclusiveProteins) {
		return this.getNumDifferentProteinGroups(countNonConclusiveProteins);
	}

	@Override
	public double getAverageNumDifferentPeptides(boolean distPeptides) {
		return this.getNumDifferentPeptides(distPeptides);
	}

	@Override
	public double getStdNumDifferentProteinGroups(
			boolean countNonConclusiveProteins) {
		return 0.0;
	}

	@Override
	public double getStdNumDifferentPeptides(boolean distPeptides) {
		return 0.0;
	}

	@Override
	public List getNextLevelIdentificationSetList() {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean hasPeptide(String sequence, boolean distModPep) {
		return this.dataManager.hasPeptide(sequence, distModPep);
	}

	@Override
	public List<String> getProteinScoreNames() {
		return this.dataManager.getProteinScores();
	}

	@Override
	public List<String> getPeptideScoreNames() {
		return this.dataManager.getPeptideScores();
	}

	@Override
	public List<String> getDifferentPeptideModificationNames() {
		final List<String> differentPeptideModificationNames = this.dataManager
				.getDifferentPeptideModificationNames();
		Collections.sort(differentPeptideModificationNames);
		return differentPeptideModificationNames;
	}

	@Override
	public int getModificatedSiteOccurrence(String modif) {
		return this.dataManager.getModificatedSiteOccurrence(modif);
	}

	@Override
	public HashMap<Integer, Integer> getModificationOccurrenceDistribution(
			String modif) {
		return this.dataManager.getModificationOccurrenceDistribution(modif);
	}

	@Override
	public DataManager getDataManager() {
		return this.dataManager;
	}

	@Override
	public int getProteinGroupTP(HashSet<String> truePositiveProteinACCs,
			boolean countNonConclusiveProteins) {
		return this.dataManager.getProteinGroupTP(truePositiveProteinACCs,
				countNonConclusiveProteins);
	}

	@Override
	public int getProteinGroupFN(HashSet<String> truePositiveProteinACCs,
			boolean countNonConclusiveProteins) {
		return this.dataManager.getProteinGroupFN(truePositiveProteinACCs,
				countNonConclusiveProteins);
	}

	@Override
	public int getProteinGroupTN(HashSet<String> truePositiveProteinACCs,
			boolean countNonConclusiveProteins) {
		return this.dataManager.getProteinGroupTN(truePositiveProteinACCs,
				countNonConclusiveProteins);
	}

	@Override
	public int getProteinGroupFP(HashSet<String> truePositiveProteinACCs,
			boolean countNonConclusiveProteins) {
		return this.dataManager.getProteinGroupFP(truePositiveProteinACCs,
				countNonConclusiveProteins);
	}

	@Override
	public void removeFilters() {
		this.dataManager.removeFilters();
	}

	@Override
	public String getName() {
		return this.replicateName;
	}

	@Override
	public List<DataManager> getNextLevelDataManagers() {
		throw new UnsupportedOperationException();
	}

	@Override
	public Experiment getPreviousLevelIdentificationSet() {
		return this.previousLevelIdentificationSet;
	}

	public void setPreviousLevelIdentificationSet(
			Experiment previousLevelIdentificationSet) {
		this.previousLevelIdentificationSet = previousLevelIdentificationSet;
	}

	// @Override
	// public FDRFilter getFDRFilter() {
	// return this.dataManager.getFDRFilter();
	// }

	@Override
	public float getProteinFDR(String scoreName) {
		return this.dataManager.getProteinFDR(scoreName);
	}

	@Override
	public float getPeptideFDR(String scoreName) {
		return this.dataManager.getPeptideFDR(scoreName);
	}

	@Override
	public float getPSMFDR(String scoreName) {
		return this.dataManager.getPSMFDR(scoreName);
	}

	@Override
	public ProteinGroupOccurrence getProteinGroupOccurrence(
			ProteinGroup proteinGroup) {
		return this.dataManager.getProteinGroupOccurrence(proteinGroup);
	}

	@Override
	public PeptideOccurrence getPeptideOccurrence(String sequence,
			boolean distinguishModPep) {
		return this.dataManager.getPeptideOccurrence(sequence,
				distinguishModPep);
	}

	@Override
	public HashMap<Integer, Integer> getMissedCleavagesOccurrenceDistribution() {
		return this.dataManager.getMissedCleavagesOccurrenceDistribution();
	}

	@Override
	public int getPeptideModificatedOccurrence(String modification) {
		return this.dataManager.getPeptideModificatedOccurrence(modification);
	}

	@Override
	public void setFilters(List filters) {
		this.dataManager.setFilters(filters);
	}

	@Override
	public double getAverageTotalNumProteinGroups(
			boolean countNonConclusiveProteins) {
		return this.dataManager
				.getTotalNumProteinGroups(countNonConclusiveProteins);
	}

	@Override
	public double getAverageTotalNumPeptides() {
		return this.dataManager.getTotalNumPeptides();
	}

	@Override
	public int getTotalNumPeptides() {
		return this.dataManager.getTotalNumPeptides();
	}

	@Override
	public double getAverageTotalVsDifferentNumProteinGroups(
			boolean countNonConclusiveProteins) {
		return getTotalVsDifferentNumProteinGroups(countNonConclusiveProteins);
	}

	@Override
	public double getTotalVsDifferentNumProteinGroups(
			boolean countNonConclusiveProteins) {
		final int totalNumProteins = this
				.getTotalNumProteinGroups(countNonConclusiveProteins);
		if (totalNumProteins > 0)
			return Double.valueOf(this
					.getNumDifferentProteinGroups(countNonConclusiveProteins))
					/ Double.valueOf(totalNumProteins);
		return 0.0;
	}

	@Override
	public double getAverageTotalVsDifferentNumPeptides(boolean distModPep) {
		return getTotalVsDifferentNumPeptides(distModPep);
	}

	@Override
	public double getTotalVsDifferentNumPeptides(boolean distModPep) {
		final int totalNumPeptides = this.getTotalNumPeptides();
		if (totalNumPeptides > 0)
			return Double.valueOf(this.getNumDifferentPeptides(distModPep))
					/ Double.valueOf(totalNumPeptides);
		return 0.0;
	}

	@Override
	public double getStdTotalVsDifferentNumProteinGroups(
			boolean countNonConclusiveProteins) {
		return 0.0;
	}

	@Override
	public double getStdTotalVsDifferentNumPeptides(Boolean distModPeptides) {
		return 0.0;
	}

	@Override
	public double getStdTotalNumProteinGroups(boolean countNonConclusiveProteins) {
		return 0.0;
	}

	@Override
	public double getStdTotalNumPeptides() {
		return 0.0;
	}

	@Override
	public Set<String> getDifferentSearchedDatabases() {
		return this.dataManager.getDifferentSearchedDatabases();
	}

	@Override
	public List<ResultingData> getResultingDatas() {
		List<ResultingData> ret = new ArrayList<ResultingData>();

		if (miapeMSs != null) {
			for (MiapeMSDocument miapeMSDocument : miapeMSs) {
				List<ResultingData> resultingDatas = miapeMSDocument
						.getResultingDatas();
				if (resultingDatas != null) {
					ret.addAll(resultingDatas);
				}
			}
		}

		return ret;
	}

	@Override
	public List<Filter> getFilters() {
		return this.dataManager.getFilters();
	}

	@Override
	public int getNumProteinGroupDecoys() {
		return this.dataManager.getNumProteinGroupDecoys();
	}

	@Override
	public int getNumDifferentProteinGroupsDecoys() {
		return this.dataManager.getNumDifferentProteinGroupsDecoys();
	}

	@Override
	public int getNumPeptideDecoys() {
		return this.dataManager.getNumPeptideDecoys();
	}

	@Override
	public int getNumDifferentPeptideDecoys(
			boolean distringuishModificatedPeptides) {
		return this.dataManager
				.getNumDifferentPeptideDecoys(distringuishModificatedPeptides);
	}

	@Override
	public int getPeptideTP(HashSet<String> positivePeptideSequences,
			boolean distinguisModificatedPeptides) {
		return this.dataManager.getPeptideTP(positivePeptideSequences,
				distinguisModificatedPeptides);
	}

	@Override
	public int getPeptideFN(HashSet<String> positivePeptideSequences,
			boolean distinguisModificatedPeptides) {
		return this.dataManager.getPeptideFN(positivePeptideSequences,
				distinguisModificatedPeptides);
	}

	@Override
	public int getPeptideTN(HashSet<String> positivePeptideSequences,
			boolean distinguisModificatedPeptides) {
		return this.dataManager.getPeptideTN(positivePeptideSequences,
				distinguisModificatedPeptides);
	}

	@Override
	public int getPeptideFP(HashSet<String> positivePeptideSequences,
			boolean distinguisModificatedPeptides) {
		return this.dataManager.getPeptideFP(positivePeptideSequences,
				distinguisModificatedPeptides);
	}

	@Override
	public ProteinGroupOccurrence getProteinGroupOccurrence(String proteinACC) {
		return this.dataManager.getProteinGroupOccurrence(proteinACC);
	}

	@Override
	public int getProteinGroupOccurrenceNumber(String proteinACC) {
		return this.dataManager.getProteinGroupOccurrenceNumber(proteinACC);
	}

	@Override
	public FDRFilter getFDRFilter() {
		return this.dataManager.getFDRFilter();
	}

	@Override
	public ControlVocabularyManager getCvManager() {
		return this.cvManager;
	}

	@Override
	public void setFiltered(boolean b) {
		this.dataManager.setFiltered(b);

	}

	public List<ResultingData> getPeakListResultingDatas() {

		if (miapeMSs != null) {
			List<ResultingData> ret = new ArrayList<ResultingData>();
			for (MiapeMSDocument miapeMS : miapeMSs) {
				List<ResultingData> resultingDatas = miapeMS
						.getResultingDatas();
				if (resultingDatas != null) {
					for (ResultingData resultingData : resultingDatas) {
						if (isPeakList(resultingData))
							ret.add(resultingData);
					}
				}
			}
			return ret;
		}
		return null;
	}

	private boolean isPeakList(ResultingData resultingData) {
		if (resultingData != null) {
			String dataFileType = resultingData.getDataFileType();
			if (dataFileType != null) {
				if (MSFileType.getInstance(cvManager).isPeakListFileType(
						dataFileType, cvManager))
					return true;
			}
		}
		return false;
	}

	public List<ResultingData> getRawFileResultingDatas() {
		if (miapeMSs != null) {
			List<ResultingData> ret = new ArrayList<ResultingData>();
			for (MiapeMSDocument miapeMS : miapeMSs) {
				List<ResultingData> resultingDatas = miapeMS
						.getResultingDatas();
				if (resultingDatas != null) {
					for (ResultingData resultingData : resultingDatas) {
						if (!isPeakList(resultingData))
							ret.add(resultingData);
					}
				}
			}
			return ret;
		}
		return null;
	}

	public List<String> getMSIGeneratedFiles() {
		if (miapeMSIs != null) {
			List<String> ret = new ArrayList<String>();
			for (MiapeMSIDocument miapeMSI : miapeMSIs) {
				if (miapeMSI.getGeneratedFilesURI() != null)
					ret.add(miapeMSI.getGeneratedFilesURI());
			}
			return ret;
		}
		return null;
	}

	public HashMap<Integer, URL> getMSReportURLs(int userId) {
		HashMap<Integer, URL> reports = new HashMap<Integer, URL>();
		for (MiapeMSDocument miapeMS : miapeMSs) {
			URL miapePublicLink = MiapeReportsLinkGenerator.getMiapePublicLink(
					userId, miapeMS.getId(), "MS");
			reports.put(miapeMS.getId(), miapePublicLink);
		}
		return reports;
	}

	public HashMap<Integer, URL> getMSIReportURLs(int userId) {
		HashMap<Integer, URL> reports = new HashMap<Integer, URL>();

		for (MiapeMSIDocument miapeMSI : miapeMSIs) {
			URL miapePublicLink = MiapeReportsLinkGenerator.getMiapePublicLink(
					userId, miapeMSI.getId(), "MSI");
			reports.put(miapeMSI.getId(), miapePublicLink);
		}
		return reports;
	}

	@Override
	public List<ExtendedIdentifiedProtein> getIdentifiedProteins() {
		return this.dataManager.getIdentifiedProteins();
	}

	@Override
	public List<ProteinGroup> getNonFilteredIdentifiedProteinGroups() {
		return this.dataManager.getNonFilteredIdentifiedProteinGroups();
	}

	@Override
	public List<ExtendedIdentifiedPeptide> getNonFilteredIdentifiedPeptides() {
		return this.dataManager.getNonFilteredIdentifiedPeptides();
	}

	@Override
	public int getTotalNumNonConclusiveProteinGroups() {
		return this.dataManager.getNumNonConclusiveProteinGroups();
	}

	@Override
	public int getNumDifferentNonConclusiveProteinGroups() {
		return this.dataManager.getNumDifferentNonConclusiveProteinGroups();
	}

	@Override
	public Set<Spectrometer> getSpectrometers() {
		final List<MiapeMSDocument> miapeMSs = this.getMiapeMSs();
		Set<Spectrometer> spectrometers = new HashSet<Spectrometer>();
		for (MiapeMSDocument miapeMSDocument : miapeMSs) {
			if (miapeMSDocument.getSpectrometers() != null)
				spectrometers.addAll(miapeMSDocument.getSpectrometers());
		}
		return spectrometers;
	}

	@Override
	public Set<InputParameter> getInputParameters() {
		final List<MiapeMSIDocument> miapeMSIs = this.getMiapeMSIs();
		Set<InputParameter> inputParameters = new HashSet<InputParameter>();
		for (MiapeMSIDocument miapeMSIDocument : miapeMSIs) {
			if (miapeMSIDocument.getInputParameters() != null)
				inputParameters.addAll(miapeMSIDocument.getInputParameters());
		}
		return inputParameters;
	}

}
